/*
	Copyright (c) 2010 Canadensys
*/
package net.canadensys.dataportal.vascan.downloads;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import net.canadensys.dataportal.utils.HibernateUtil;
import net.canadensys.dataportal.vascan.LookupManager;
import net.canadensys.dataportal.vascan.config.ApplicationConfig;

/**
 * This class is the entry point to create DarwinCore archive file.
 * Files will be created in the folder specified in Config.DARWIN_CORE_PATH.
 */
public class DarwinCoreArchiveManager {
	private ResourceBundle _bundle = null;
	
	/**
	 * Create DarwinCoreArchiveManager with default language (english)
	 * @param bundle
	 */
	public DarwinCoreArchiveManager(ResourceBundle bundle){
		this(bundle,null);
	}
	
	/**
	 * @param bundle a current loaded bundle or null
	 * @param language the wanted language or null (default en)
	 */
	public DarwinCoreArchiveManager(ResourceBundle bundle, String language){
		if(bundle != null){
			_bundle = bundle;
		}
		else if (language == null){
			 _bundle = ResourceBundle.getBundle("vascan", new Locale("en"));
		}
		else{
			try{
			    _bundle = ResourceBundle.getBundle("vascan", new Locale(language));
			}catch(MissingResourceException e){
			    _bundle = ResourceBundle.getBundle("vascan", new Locale("en"));
			}
		}
	}
	
	/**
	 * Method to generate a complete DarwinCore archive from the root taxon.
	 * @param destinationFilePath absolute path for destination file
	 * @param metaXMLPath absolute path to meta.xml file
	 * @return success
	 */
	public boolean generateCompleteDarwinCoreArchive(String destinationFilePath, String metaXMLPath){
	    /* sort */
	    String sort = "taxonomically";
	    
	    return generateDarwinCoreArchive(destinationFilePath, metaXMLPath, null, null, null, ApplicationConfig.ROOT_TAXON_ID, null,
	    		null, Boolean.TRUE.toString(), sort);
	}
	
	/**
	 * Method to generate a DarwinCore archive from the parameters
	 * @param destinationFilePath absolute path for destination file
	 * @param metaXMLPath absolute path to meta.xml file
	 * @param province
	 * @param combination
	 * @param habit
	 * @param taxonId
	 * @param distributionStatus
	 * @param rank
	 * @param hybrids
	 * @param sort
	 * @return
	 */
	public boolean generateDarwinCoreArchive(String destinationFilePath, String metaXMLPath,
			String[] province, String combination, String habit, Integer taxonId, String[] distributionStatus,
			String[] rank, String hybrids, String sort){

		if(destinationFilePath == null || destinationFilePath.trim().equals("")){
			return false;
		}
		   
	    // string buffers that will contain dwc mapped data
	    StringBuffer buffer = new StringBuffer("");
	    StringBuffer taxonDwc = new StringBuffer("");
	    StringBuffer synonymDwc = new StringBuffer("");
	    StringBuffer distributionDwc = new StringBuffer("");
	    StringBuffer vernacularDwc = new StringBuffer("");
	    StringBuffer habitusDwc = new StringBuffer("");
	    String newline = "\n";
	    
	    // meta.xml file
	    File meta = new File(metaXMLPath);
	    
	    Set<Integer> synonyms = new HashSet<Integer>();
	    boolean success = true;

	    boolean displayHeader = true;
	    
	    ArrayList<Integer> inClause = new ArrayList<Integer>();
	    
	    org.hibernate.Session hibernateSession = HibernateUtil.getSessionFactory().getCurrentSession();
	    hibernateSession.beginTransaction();

    	boolean returnCountOnly = false;
        int limitResultsTo = 0;
        ResultSet rs = LookupManager.getDistributions(returnCountOnly, limitResultsTo, combination, habit, taxonId, province, distributionStatus, rank, hybrids, sort);
       
        if(rs !=null){
        	try{
	            rs.beforeFirst();
	            
	            // take the list of all taxon id and create a sql clause in the form of "... AND taxonid IN (-->1,2,3,52<--) ..."
	            while(rs.next()){
	                inClause.add((Integer) rs.getInt("taxonid"));
	            }
        	}
        	catch (SQLException e) {
				return false;
			}
        	finally{
        		if(rs != null){
        			try {
						rs.close();
					} catch (SQLException e) {}
        		}
        	}
            // TAXON
            buffer = DarwinCoreArchiveUtils.generateTaxonDwc(inClause.toArray(),_bundle,displayHeader);
            taxonDwc.append(buffer);
            // for each taxon, find the children synonyms
            // these synonyms must be added at the end of the taxon.txt file
            
            for(Integer synonym : DarwinCoreArchiveUtils.getSynonymsForParentId(inClause.toArray())){
                if(!inClause.contains(synonym))
                    synonyms.add(synonym);
            }
            
            // DISTRIBUTION
            buffer = DarwinCoreArchiveUtils.generateDistributionDwc(inClause.toArray(),_bundle,displayHeader);
            distributionDwc.append(buffer);
	            
            // VERNACULAR
            buffer = DarwinCoreArchiveUtils.generateVernacularDwc(inClause.toArray(),_bundle,displayHeader);
            vernacularDwc.append(buffer);
            
            // HABITUS
            buffer = DarwinCoreArchiveUtils.generateHabitusDwc(inClause.toArray(),_bundle,displayHeader);
            habitusDwc.append(buffer);
	        
            
            // once the header is added, we don't need it anymore
            displayHeader = false;
            
            if(synonyms.size() > 0){
                buffer = DarwinCoreArchiveUtils.generateTaxonDwc(synonyms.toArray(),_bundle,displayHeader);
                synonymDwc.append(buffer);
            }
        }


	    // append synonym at the end of taxon.txt file
	    taxonDwc.append(synonymDwc.toString());

        File tmp = new File(destinationFilePath);
        FileReader metaReader;
        BufferedWriter bw = null;
		try {
			metaReader = new FileReader(meta);
		
	        BufferedReader metaBuffered = new BufferedReader(metaReader);
	        StringBuffer metaData = new StringBuffer("");
	        String line = "";
	        while((line = metaBuffered.readLine()) != null){
	            metaData.append(line).append(newline);
	        }
	        String[] data = {taxonDwc.toString(), distributionDwc.toString(), vernacularDwc.toString(), habitusDwc.toString(), metaData.toString()};
	        String[] files = {ApplicationConfig.DARWIN_CORE_FILE_TAXON, ApplicationConfig.DARWIN_CORE_FILE_DISTRIBUTION, ApplicationConfig.DARWIN_CORE_FILE_VERNACULAR, ApplicationConfig.DARWIN_CORE_FILE_HABITUS, ApplicationConfig.DARWIN_CORE_FILE_META};
	        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tmp));
	        bw = new BufferedWriter(new OutputStreamWriter(zos, "UTF8"));
	
	        for (int i = 0; i < data.length; ++i) {
	            ZipEntry entry = new ZipEntry(files[i]);
	            zos.putNextEntry(entry);
	            bw.write(data[i]);
	            bw.flush();
	            zos.closeEntry();
	        }
	        bw.close();
	        bw = null;
		} catch (FileNotFoundException e) {
			success = false;
			e.printStackTrace();
		} catch (IOException e) {
			success = false;
			e.printStackTrace();
		}
		finally{
			if(bw != null){
				try {
					bw.close();
				} catch (IOException e) {}
			}
		}
		hibernateSession.getTransaction().commit();
        return success;
	}
}
